﻿using System;
using System.Collections.Generic;
using System.Text;

using NUnit.Framework;

using vJine.Core.ORM;
using vJineCore_QuickStart.BML;

namespace vJineCore_QuickStart.UT {
    [TestFixture]
    public class UT_QuickStart_ORM {
        [Test(Description = "DataManager:实例")]
        public static void UT_00_00_MANAGER([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            DataManager dm = new DataManager(db);
            System.Console.WriteLine("Adapter:" + dm.Adapter.Name);
            System.Console.WriteLine("Provider:" + dm.Provider.ToString());
        }

        [Test(Description = "类型测试:BOOL")]
        public static void UT_02_01_TYPES_BOOL([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<bool>(db, true, false);
        }

        [Test(Description = "类型测试:SBYTE")]
        public static void UT_02_02_TYPES_SBYTE([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<sbyte>(db, sbyte.MinValue, sbyte.MaxValue);
        }

        [Test(Description = "类型测试:BYTE")]
        public static void UT_02_03_TYPES_BYTE([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<byte>(db, byte.MinValue, byte.MaxValue);
        }

        [Test(Description = "类型测试:SHORT")]
        public static void UT_02_04_TYPES_SHORT([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<short>(db, short.MinValue, short.MaxValue);
        }

        [Test(Description = "类型测试:USHORT")]
        public static void UT_02_05_TYPES_USHORT([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<ushort>(db, ushort.MinValue, ushort.MaxValue);
        }

        [Test(Description = "类型测试:INT")]
        public static void UT_02_06_TYPES_INT([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<int>(db, int.MinValue, int.MaxValue);
        }

        [Test(Description = "类型测试:UINT")]
        public static void UT_02_07_TYPES_UINT([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<uint>(db, uint.MinValue, uint.MaxValue);
        }

        [Test(Description = "类型测试:LONG")]
        public static void UT_02_08_TYPES_LONG([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<long>(db, long.MinValue, long.MaxValue);
        }

        [Test(Description = "类型测试:ULONG")]
        public static void UT_02_09_TYPES_ULONG([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<ulong>(db, ulong.MinValue, ulong.MaxValue);
        }

        [Test(Description = "类型测试:FLOAT")]
        public static void UT_02_10_TYPES_FLOAT([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<float>(db, float.MinValue, float.MaxValue);
        }

        [Test(Description = "类型测试:DOUBLE")]
        public static void UT_02_11_TYPES_DOUBLE([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<double>(db, double.MinValue, double.MaxValue);
        }

        [Test(Description = "类型测试:DECIMAL")]
        public static void UT_02_12_TYPES_DECIMAL([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<decimal>(db, decimal.MinValue, decimal.MaxValue);
        }

        [Test(Description = "类型测试:CHAR")]
        public static void UT_02_13_TYPES_CHAR([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<char>(db, char.MinValue, char.MaxValue);
        }

        [Test(Description = "类型测试:GUID")]
        public static void UT_02_13_TYPES_GUID([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<Guid>(db, Guid.NewGuid(), Guid.NewGuid());
        }

        [Test(Description = "类型测试:DATETIME")]
        public static void UT_02_13_TYPES_DATETIME([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<DateTime>(db, DateTime.Now, DateTime.Now);
        }

        [Test(Description = "类型测试:枚举")]
        public static void UT_02_17_TYPES_ENUM([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type<BYTE>(db, BYTE.Default, BYTE.Hello | BYTE.World);
            test_type<SHORT>(db, SHORT.Default, SHORT.Hello | SHORT.World);
            test_type<USHORT>(db, USHORT.Default, USHORT.Hello | USHORT.World);
            test_type<INT>(db, INT.Default, INT.Hello | INT.World);
            test_type<UINT>(db, UINT.Default, UINT.Hello | UINT.World);
            test_type<LONG>(db, LONG.Default, LONG.Hello | LONG.World);
            test_type<ULONG>(db, ULONG.Default, ULONG.Hello | ULONG.World);
        }

        [Test(Description = "类型测试:字符串")]
        public static void UT_02_18_STRING([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type_string(db, "ABC", "中文简体");
        }

        [Test(Description = "类型测试:字节数组")]
        public static void UT_02_19_BYTES([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            test_type_bytes(db, new byte[] { 0x01, 0x02, 0x03 }, new byte[] { 0x0A, 0x0B, 0x0C });
        }

        static void test_type<T>(string db, T min, T max) where T : struct {
            TYPES<T> entity_min = new TYPES<T>() { F = min, N = null };
            TYPES<T> entity_max = new TYPES<T>() { F = max, N = max };

            DataManager dm = new DataManager(db);
            string table_name = TYPES<T>.Name;

            dm.Open();
            try {
                dm.Create<TYPES<T>>(table_name);
                dm.I<TYPES<T>>(entity_min, table_name);
                dm.I<TYPES<T>>(entity_max, table_name);
                TYPES<T> MIN = dm.Q<TYPES<T>>(TYPES<T>._.N.IsNull(), table_name);
                TYPES<T> MAX = dm.Q<TYPES<T>>(TYPES<T>._.N.IsNotNull(), table_name);
                Assert.AreEqual(min, MIN.F, "MIN_D");
                Assert.AreEqual(null, MIN.N, "MIN_D_NULL");
                Assert.AreEqual(max, MAX.F, "MAX_D");
                Assert.AreEqual(max, MAX.N, "MAX_D_NULL");
            } catch(Exception ex) {
                throw ex;
            } finally {
                dm.Drop<TYPES<T>>(table_name);
                dm.Close();
            }
        }

        static void test_type_string(string db, string min, string max) {
            TYPES_STRING entity_min = new TYPES_STRING() { F = min, N = null };
            TYPES_STRING entity_max = new TYPES_STRING() { F = max, N = max };

            DataManager dm = new DataManager(db);
            string table_name = TYPES_STRING.Name;

            dm.Open();
            try {
                dm.Create<TYPES_STRING>(table_name);
                dm.I<TYPES_STRING>(entity_min, table_name);
                dm.I<TYPES_STRING>(entity_max, table_name);
                TYPES_STRING MIN = dm.Q<TYPES_STRING>(TYPES_STRING._.N.IsNull(), table_name);
                TYPES_STRING MAX = dm.Q<TYPES_STRING>(TYPES_STRING._.N.IsNotNull(), table_name);
                Assert.AreEqual(min, MIN.F, "MIN_D");
                Assert.AreEqual(null, MIN.N, "MIN_D_NULL");
                Assert.AreEqual(max, MAX.F, "MAX_D");
                Assert.AreEqual(max, MAX.N, "MAX_D_NULL");
            } catch(Exception ex) {
                throw ex;
            } finally {
                dm.Drop<TYPES_STRING>(table_name);
                dm.Close();
            }
        }

        static void test_type_bytes(string db, byte[] min, byte[] max) {
            TYPES_BYTES entity_min = new TYPES_BYTES() { F = min, N = null };
            TYPES_BYTES entity_max = new TYPES_BYTES() { F = max, N = max };

            DataManager dm = new DataManager(db);
            string table_name = TYPES_BYTES.Name;

            dm.Open();
            try {
                dm.Create<TYPES_BYTES>(table_name);
                dm.I<TYPES_BYTES>(entity_min, table_name);
                dm.I<TYPES_BYTES>(entity_max, table_name);
                TYPES_BYTES MIN = dm.Q<TYPES_BYTES>(TYPES_BYTES._.N.IsNull(), table_name);
                TYPES_BYTES MAX = dm.Q<TYPES_BYTES>(TYPES_BYTES._.N.IsNotNull(), table_name);
                Assert.AreEqual(min, MIN.F, "MIN_D");
                Assert.AreEqual(null, MIN.N, "MIN_D_NULL");
                Assert.AreEqual(max, MAX.F, "MAX_D");
                Assert.AreEqual(max, MAX.N, "MAX_D_NULL");
            } catch(Exception ex) {
                throw ex;
            } finally {
                dm.Drop<TYPES_BYTES>(table_name);
                dm.Close();
            }
        }

        [Test]
        public static void UT_03_00_BASE([Values("SQLite", "ACCESS", "MySQL", "PGSQL", "MSSQL", "Oracle")] string db) {
            DataManager dm = new DataManager(db);

            DateTime db_now = dm.Now; //读取数据库时间
            string version = dm.Version;

            dm.Open();
            try {
                //dm.Drop<Orm_BaseTypes>();
                dm.Create<MyEntity>(); //创建表不受事务控制
                {
                    Assert.AreEqual(0, dm.Count<MyEntity>());
                    Assert.AreEqual(false, dm.Exist<MyEntity>());
                }
                string diffMsg = null;

                MyEntity orm_min = new MyEntity(false);
                {
                    dm.I<MyEntity>(orm_min);
                    Assert.AreEqual(1, dm.Count<MyEntity>());
                    Assert.AreEqual(true, dm.Exist<MyEntity>(MyEntity._.F_BOOL_NULL.IsNull()));
                }

                MyEntity orm_max = new MyEntity(true);
                {
                    dm.I<MyEntity>(orm_max);
                    Assert.AreEqual(2, dm.Count<MyEntity>());
                    Assert.AreEqual(true, dm.Exist<MyEntity>(MyEntity._.F_BOOL_NULL.IsNotNull()));
                }

                MyEntity orm_MIN = dm.Q<MyEntity>(MyEntity._.F_BOOL_NULL.IsNull()); //Min
                diffMsg = Compare<MyEntity>(orm_MIN, orm_min);
                Assert.AreEqual(diffMsg, "", "MIN DATA ERROR");

                MyEntity orm_MAX = dm.Q<MyEntity>(MyEntity._.F_BOOL_NULL.IsNotNull()); //Max
                diffMsg = Compare<MyEntity>(orm_MAX, orm_max);
                Assert.AreEqual(diffMsg, "", "MAX DATA ERROR");
            } catch(Exception ex) {
                throw ex;
            } finally {
                dm.Drop<MyEntity>();
                dm.Close(); dm = null;
            }
        }

        public static string Compare<T>(T objSrc, T objDst) {
            StringBuilder sbResult = new StringBuilder();

            Class<T>.Property[] P = Class<T>.GetMap();
            for (int i = 0, len = P.Length; i < len; i++) {
                Class<T>.Property p_i = P[i];
                object objVsrc = p_i.Get(objSrc);
                object objVdst = p_i.Get(objDst);
                string vSrc = objVsrc == null ? null : objVsrc.ToString();
                string vDst = objVdst == null ? null : objVdst.ToString();
                if (vSrc != vDst) {
                    sbResult.AppendFormat("<{0}>: [{1}] != [{2}]", p_i.Name, vSrc, vDst).AppendLine();
                }
            }
            if (sbResult.Length == 0) {
                return "";
            } else {
                return sbResult.ToString();
            }
        }

    }
}
